using System;
using System.Collections;
using Sedna.Core.Persistence;
using Sedna.Core.Persistence.Hql;
using Sedna.Util;
using log4net;
using NHibernate;
using NHibernate.Metadata;

namespace Sedna.DAO
{
	public class HbmBaseDAO : IBaseDAO
	{
		private ILog log = LogManager.GetLogger(typeof(HbmBaseDAO));

		protected ITxManager txManager;

		public ITxManager TxManager
		{
			set { txManager = value; }
		}

		public virtual ISession Session
		{
		    get
		    {
		        return NHibernateHelper.GetCurrentSession();
		    }
		}

		public virtual object GetById(Type type, object id)
		{
			return Session.Load(type, id);
		}

        public virtual bool Contains(object entity)
	    {
            return Session.Contains(entity);
	    }

	    [Transactional]
		public virtual void SaveOrUpdate(object entity)
		{
			Session.SaveOrUpdate(entity);
		}

		[Transactional]
        public virtual void Update(object detachedEntity)
		{
            Session.Update(detachedEntity);
		}

        public virtual void TryToUpdate(object entity)
	    {
            if (!Contains(entity))
                Update(entity);
	    }

        [Transactional]
        public virtual void Refresh(object entity)
	    {
            Session.Refresh(entity);
	    }

	    [Transactional]
		public virtual void Delete(object entity)
		{
			Session.Delete(entity);
		}

		[Transactional]
		public virtual void Delete(Type type, object id)
		{
			IClassMetadata classMetadata = Session.SessionFactory.GetClassMetadata(type);
			Type mappedClass = classMetadata.GetMappedClass(EntityMode.Map);
			string hqlQuery = String.Format("from {0} as entity where entity.{1}=?", mappedClass.FullName, classMetadata.IdentifierPropertyName);
			if (log.IsDebugEnabled)
			{
				log.Debug(String.Format("executing delete query '{0}'", hqlQuery));
			}
			Session.Delete(hqlQuery, id, classMetadata.IdentifierType);
		}

        //[Sessional(false)]
        public virtual int AttachToSession(object entity)
	    {
            log.Debug(String.Format("Attaching object: {0}", entity));
            
            try
	        {                	            
                if (entity is ICollection)
                {
                    int attached = 0;
                    
                    foreach (Object obj in entity as ICollection)
                    {
                        attached += AttachToSession(obj);
                    }

                    return attached;
                }
                else if (Contains(entity))
                {
                    log.Debug("Entity has been attached already");

                    return 0;
                } 
                else if (!Utility.IsPersisted(entity))
                {
                    log.Warn("Can't attach instance that is not persistent");
                    
                    return 0;
                }
	            
                Session.Lock(entity, LockMode.None);
                
	            return 1;
	        }
	        catch(Exception e)
	        {
                if (log.IsErrorEnabled)
                {
                    log.Error("Error occured while attaching object to hibernate session", e);
                }
                
	            return 0;	            
	        }
	    }

	    public FlushMode SetSessionNoFlush()
	    {
            FlushMode result = Session.FlushMode;
            Session.FlushMode = FlushMode.Never;
            return result;
	    }

	    public void RestoreSessionFlushMode(FlushMode flushMode)
	    {
            Session.FlushMode = flushMode;
	    }
	}
}
